/**
 * 2025/01/01 - 2025/12/31
 */
package com.snail.server.database.engine.datasource.manager;

import cn.hutool.core.codec.Base64;
import cn.hutool.json.JSONUtil;
import com.snail.server.database.engine.datasource.dto.DataSourceAddDTO;
import com.snail.server.database.engine.datasource.dto.DataSourceModifyDTO;
import com.snail.server.database.engine.datasource.dto.DataSourceTestDTO;
import com.snail.server.database.engine.datasource.entity.DataSource;
import com.snail.server.database.engine.datasource.enums.DataSourceTypeEnum;
import com.snail.server.database.engine.datasource.holder.DynamicDatasourceHolder;
import com.snail.server.database.engine.datasource.mapping.DataSourceMapping;
import com.snail.server.database.engine.datasource.service.DataSourceService;
import com.snail.server.database.engine.datasource.vo.DataSourceVO;
import com.snail.starter.commons.error.CommonErrorCode;
import com.snail.starter.commons.exception.BizException;
import com.snail.server.database.engine.error.DatabaseEngineErrorCode;
import com.snail.starter.db.enums.EnableOrDisEnableEnum;
import com.snail.starter.db.holder.DynamicTransactionTemplate;
import com.snail.starter.utils.id.ObjectId;
import com.snail.starter.utils.lambda.GenericEntityBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author zgadmin
 * @date 2025/8/28 17:21
 */
@Slf4j
@Component
public class DataSourceManager {

    @Resource
    DataSourceService dataSourceService;

    @Resource
    DataSourceMapping dataSourceMapping;

    @Resource
    DynamicDatasourceHolder dynamicDatasourceHolder;

    @Resource
    DynamicTransactionTemplate dynamicTransactionTemplate;

    public Long add(DataSourceAddDTO datasourceAddDTO) {
        DataSource datasource = GenericEntityBuilder.of(DataSource::new)
                .withSupplier(DataSource::setDataSourceId, ObjectId::nextLongId)
                .withSupplier(DataSource::setAppKey, datasourceAddDTO::getAppKey)
                .withSupplier(DataSource::setName, datasourceAddDTO::getName)
                .withSupplier(DataSource::setUrl, datasourceAddDTO::getUrl)
                .withSupplier(DataSource::setUsername, datasourceAddDTO::getUsername)
                .withSupplier(DataSource::setPassword, datasourceAddDTO::getPassword)
                .withSupplier(DataSource::setType, () -> DataSourceTypeEnum.getEnums(datasourceAddDTO.getUrl()))
                .withSupplier(DataSource::setStatus, () -> EnableOrDisEnableEnum.ENABLE)
                .withSupplier(DataSource::setPool, () -> null == datasourceAddDTO.getPool() || datasourceAddDTO.getPool().isEmpty() ? "" : Base64.encode(JSONUtil.toJsonStr(datasourceAddDTO.getPool())))
                .build();
        dynamicTransactionTemplate.doTransactionWithThrowable(() -> {
            dataSourceService.save(datasource);
            dynamicDatasourceHolder.loadDataSource(datasource);
        });
        return datasource.getDataSourceId();
    }

    public Boolean test(DataSourceTestDTO datasourceTestDTO) {
        if (null == datasourceTestDTO.getPassword() || "".equals(datasourceTestDTO.getPassword())) {
            if (null == datasourceTestDTO.getDatasourceId()) {
                throw BizException.of(CommonErrorCode.MISS_PARAM_ERROR, new Object[]{"datasourceId"});
            }
            DataSource datasource = dataSourceService.queryById(datasourceTestDTO.getDatasourceId(), EnableOrDisEnableEnum.ENABLE);
            if (null == datasource) {
                throw BizException.of(DatabaseEngineErrorCode.DATASOURCE_NOT_EXISTS_OR_STATUS_ERROR, new Object[]{datasourceTestDTO.getDatasourceId(), EnableOrDisEnableEnum.ENABLE.getDesc()});
            }
            datasourceTestDTO.setPassword(datasource.getPassword());
        }
        return dynamicDatasourceHolder.testThrowable(datasourceTestDTO.getUrl(), datasourceTestDTO.getUsername(), datasourceTestDTO.getPassword());
    }

    public DataSourceVO queryById(Long dataSourceId) {
        return dataSourceMapping.dataSource2DataSourceVO(dataSourceService.queryById(dataSourceId));
    }

    public Boolean modify(Long dataSourceId, DataSourceModifyDTO dataSourceModifyDTO) {
        return dataSourceService.modify(dataSourceId, dataSourceMapping.dataSourceModify2DataSource(dataSourceModifyDTO));
    }

    public Boolean delete(Long dataSourceId) {
        dataSourceService.delete(dataSourceId);
        dynamicDatasourceHolder.unLoadDataSource(dataSourceId.toString());
        return true;
    }

    public List<DataSourceVO> list() {
        return dataSourceMapping.batchDataSource2DataSourceVO(dataSourceService.list());
    }
}